diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-27 09:44:05 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-27 09:44:05 +0000 |
| commit | 24c19f0193cab4df20510e2764c0d0fe158e3842 (patch) | |
| tree | 168ff80cf3c9bec667bd0056d3f50722582c151a /app/[lng]/partners/(partners)/_dolce-upload/dolce-upload-page.tsx | |
| parent | daabc02e9ae54f216ada77aa826b349f37c3281a (diff) | |
| parent | 5ca88c4869be338f4b0e506a679e4dc4e029d7aa (diff) | |
Merge branch 'dujinkim' of https://github.com/DTS-Development/SHI_EVCP into dujinkim
Diffstat (limited to 'app/[lng]/partners/(partners)/_dolce-upload/dolce-upload-page.tsx')
| -rw-r--r-- | app/[lng]/partners/(partners)/_dolce-upload/dolce-upload-page.tsx | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/app/[lng]/partners/(partners)/_dolce-upload/dolce-upload-page.tsx b/app/[lng]/partners/(partners)/_dolce-upload/dolce-upload-page.tsx new file mode 100644 index 00000000..1bb876fb --- /dev/null +++ b/app/[lng]/partners/(partners)/_dolce-upload/dolce-upload-page.tsx @@ -0,0 +1,420 @@ +"use client"; + +import { useState, useEffect, useCallback, useMemo } from "react"; +import { useParams } from "next/navigation"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { InfoIcon, RefreshCw, Search, Upload } from "lucide-react"; +import { toast } from "sonner"; +import { useTranslation } from "@/i18n/client"; +import { + UnifiedDwgReceiptItem, + fetchDwgReceiptList, + getVendorSessionInfo, + fetchVendorProjects, +} from "@/lib/dolce/actions"; +import { DrawingListTable } from "@/lib/dolce/table/drawing-list-table"; +import { drawingListColumns } from "@/lib/dolce/table/drawing-list-columns"; +import { createGttDrawingListColumns, DocumentType } from "@/lib/dolce/table/gtt-drawing-list-columns"; +import { DetailDrawingDialog } from "@/lib/dolce/dialogs/detail-drawing-dialog"; +// V2: MatchBatchFileDwg+MatchBatchFileDwgEdit API 사용, 별도의 RegisterKind 선택 없이 결과값 기준으로 업로드 +import { B4BulkUploadDialogV3 } from "@/lib/dolce/dialogs/b4-bulk-upload-dialog-v3"; +// V1로 되돌리려면: 위 줄을 주석 처리하고 아래 줄의 주석을 해제하세요 +// import { B4BulkUploadDialog } from "@/lib/dolce/dialogs/b4-bulk-upload-dialog"; + +interface DolceUploadPageProps { + searchParams: { [key: string]: string | string[] | undefined }; +} + +export default function DolceUploadPage({ searchParams }: DolceUploadPageProps) { + const params = useParams(); + const lng = params?.lng as string; + const { t } = useTranslation(lng, "dolce"); + + // URL에서 초기 프로젝트 코드 + const initialProjNo = (searchParams.projNo as string) || ""; + + // 상태 관리 + const [drawings, setDrawings] = useState<UnifiedDwgReceiptItem[]>([]); + const [projects, setProjects] = useState<Array<{ code: string; name: string }>>([]); + const [vendorInfo, setVendorInfo] = useState<{ + userId: string; + userName: string; + email: string; + vendorCode: string; + vendorName: string; + drawingKind: "B3" | "B4"; + } | null>(null); + const [isLoading, setIsLoading] = useState(true); + const [isRefreshing, setIsRefreshing] = useState(false); + const [error, setError] = useState<string | null>(null); + + // 필터 상태 + const [projNo, setProjNo] = useState(initialProjNo); + const [drawingNo, setDrawingNo] = useState(""); + const [drawingName, setDrawingName] = useState(""); + const [discipline, setDiscipline] = useState(""); + const [manager, setManager] = useState(""); + const [documentType, setDocumentType] = useState<DocumentType>("ALL"); // B4 전용 + + // 선택된 도면 (다이얼로그용) + const [selectedDrawing, setSelectedDrawing] = useState<UnifiedDwgReceiptItem | null>(null); + const [dialogOpen, setDialogOpen] = useState(false); + + // 일괄 업로드 다이얼로그 + const [bulkUploadDialogOpen, setBulkUploadDialogOpen] = useState(false); + + // 초기 데이터 로드 + const loadInitialData = useCallback(async () => { + try { + setIsLoading(true); + setError(null); + + // 병렬로 데이터 로드 + const [vendorInfoData, projectsData] = await Promise.all([ + getVendorSessionInfo(), + fetchVendorProjects(), + ]); + + setVendorInfo(vendorInfoData as typeof vendorInfo); + setProjects(projectsData); + + // 초기 프로젝트가 있으면 도면 로드 + if (initialProjNo) { + const drawingsData = await fetchDwgReceiptList({ + project: initialProjNo, + drawingKind: vendorInfoData.drawingKind, + drawingVendor: vendorInfoData.drawingKind === "B3" ? vendorInfoData.vendorCode : "", + }); + setDrawings(drawingsData); + } + } catch (err) { + console.error("초기 데이터 로드 실패:", err); + setError(err instanceof Error ? err.message : t("page.initialLoadError")); + toast.error(t("page.initialLoadError")); + } finally { + setIsLoading(false); + } + }, [initialProjNo, t]); + + // 도면 목록 조회 + const loadDrawings = useCallback(async () => { + if (!projNo || !vendorInfo) return; + + try { + setIsRefreshing(true); + setError(null); + + const drawingsData = await fetchDwgReceiptList({ + project: projNo, + drawingKind: vendorInfo.drawingKind, + drawingVendor: vendorInfo.drawingKind === "B3" ? vendorInfo.vendorCode : "", + }); + + setDrawings(drawingsData); + toast.success(t("page.drawingLoadSuccess")); + } catch (err) { + console.error("도면 로드 실패:", err); + setError(err instanceof Error ? err.message : t("page.drawingLoadError")); + toast.error(t("page.drawingLoadError")); + } finally { + setIsRefreshing(false); + } + }, [projNo, vendorInfo, t]); + + // 초기 데이터 로드 + useEffect(() => { + loadInitialData(); + }, [loadInitialData]); + + // 프로젝트 변경 시 자동 검색 + useEffect(() => { + if (projNo && vendorInfo) { + loadDrawings(); + } + }, [projNo, vendorInfo, loadDrawings]); + + // 도면 클릭 핸들러 + const handleDrawingClick = (drawing: UnifiedDwgReceiptItem) => { + setSelectedDrawing(drawing); + setDialogOpen(true); + }; + + // 검색 핸들러 + const handleSearch = () => { + loadDrawings(); + }; + + // 새로고침 핸들러 + const handleRefresh = () => { + loadDrawings(); + }; + + // 일괄 업로드 완료 핸들러 + const handleBulkUploadComplete = () => { + loadDrawings(); + }; + + // 필터된 도면 목록 (클라이언트 사이드 필터링) + const filteredDrawings = useMemo(() => { + let result = drawings.filter((drawing) => { + // 도면번호 필터 (공백 포함) + if (drawingNo && !drawing.DrawingNo.toLowerCase().includes(drawingNo.toLowerCase())) { + return false; + } + + // 도면명 필터 (공백 포함) + if (drawingName && !drawing.DrawingName.toLowerCase().includes(drawingName.toLowerCase())) { + return false; + } + + // 설계공종 필터 (공백 포함) + if (discipline && !drawing.Discipline?.toLowerCase().includes(discipline.toLowerCase())) { + return false; + } + + // 담당자명 필터 (공백 포함) + if (manager && !drawing.Manager.toLowerCase().includes(manager.toLowerCase()) && + !drawing.ManagerENM?.toLowerCase().includes(manager.toLowerCase())) { + return false; + } + + return true; + }); + + // B4인 경우 Document Type 필터 적용 + if (vendorInfo?.drawingKind === "B4" && documentType !== "ALL") { + result = result.filter((drawing) => { + // B4 타입 체크 + if (drawing.DrawingKind !== "B4") return false; + + // B4 도면의 DrawingMoveGbn 체크 + const gttDrawing = drawing as { DrawingMoveGbn?: string }; + + if (documentType === "SHI_INPUT") { + return gttDrawing.DrawingMoveGbn === "도면제출"; + } else if (documentType === "GTT_DELIVERABLES") { + return gttDrawing.DrawingMoveGbn === "도면입수"; + } + return true; + }); + } + + return result; + }, [drawings, drawingNo, drawingName, discipline, manager, vendorInfo?.drawingKind, documentType]); + + if (isLoading) { + return ( + <Card> + <CardHeader> + <Skeleton className="h-8 w-48" /> + <Skeleton className="h-4 w-96" /> + </CardHeader> + <CardContent className="space-y-4"> + <Skeleton className="h-32 w-full" /> + <Skeleton className="h-96 w-full" /> + </CardContent> + </Card> + ); + } + + return ( + <div className="space-y-6 max-w-full overflow-x-hidden"> + {/* 에러 메시지 */} + {error && ( + <Alert variant="destructive"> + <AlertDescription>{error}</AlertDescription> + </Alert> + )} + + {/* 안내 메시지 */} + {!projNo && ( + <Alert> + <InfoIcon className="h-4 w-4" /> + <AlertDescription> + {t("page.selectProject")} + </AlertDescription> + </Alert> + )} + + {/* 필터 카드 */} + <Card> + <CardHeader> + <CardTitle>{t("filter.title")}</CardTitle> + </CardHeader> + <CardContent> + <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> + {/* 프로젝트 선택 */} + <div className="space-y-2"> + <Label>{t("filter.project")}</Label> + <Select value={projNo} onValueChange={setProjNo}> + <SelectTrigger> + <SelectValue placeholder={t("filter.projectPlaceholder")} /> + </SelectTrigger> + <SelectContent> + {projects.map((project) => ( + <SelectItem key={project.code} value={project.code}> + {project.code} - {project.name} + </SelectItem> + ))} + </SelectContent> + </Select> + </div> + + {/* 도면번호 검색 */} + <div className="space-y-2"> + <Label>{t("filter.drawingNo")}</Label> + <Input + value={drawingNo} + onChange={(e) => setDrawingNo(e.target.value)} + placeholder={t("filter.drawingNoPlaceholder")} + /> + </div> + + {/* 도면명 검색 */} + <div className="space-y-2"> + <Label>{t("filter.drawingName")}</Label> + <Input + value={drawingName} + onChange={(e) => setDrawingName(e.target.value)} + placeholder={t("filter.drawingNamePlaceholder")} + /> + </div> + + {/* 설계공종 검색 */} + <div className="space-y-2"> + <Label>{t("filter.discipline")}</Label> + <Input + value={discipline} + onChange={(e) => setDiscipline(e.target.value)} + placeholder={t("filter.disciplinePlaceholder")} + /> + </div> + + {/* 담당자명 검색 (클라이언트 필터) */} + <div className="space-y-2"> + <Label>{t("filter.manager")}</Label> + <Input + value={manager} + onChange={(e) => setManager(e.target.value)} + placeholder={t("filter.managerPlaceholder")} + /> + </div> + + {/* B4(GTT) 전용: Document Type 필터 */} + {vendorInfo?.drawingKind === "B4" && ( + <div className="space-y-2"> + <Label>{t("filter.documentType")}</Label> + <Select value={documentType} onValueChange={(value) => setDocumentType(value as DocumentType)}> + <SelectTrigger> + <SelectValue /> + </SelectTrigger> + <SelectContent> + <SelectItem value="ALL">{t("filter.documentTypeAll")}</SelectItem> + <SelectItem value="GTT_DELIVERABLES">{t("filter.documentTypeGttDeliverables")}</SelectItem> + <SelectItem value="SHI_INPUT">{t("filter.documentTypeSHIInput")}</SelectItem> + </SelectContent> + </Select> + </div> + )} + </div> + + <div className="flex gap-2 mt-4 justify-end"> + <Button + onClick={handleSearch} + disabled={!projNo || isRefreshing} + > + <Search className="h-4 w-4 mr-2" /> + {t("filter.searchButton")} + </Button> + {/* B4 벤더인 경우에만 일괄 업로드 버튼 표시 */} + {vendorInfo?.drawingKind === "B4" && ( + <Button + variant="default" + onClick={() => setBulkUploadDialogOpen(true)} + disabled={!projNo || isRefreshing} + > + <Upload className="h-4 w-4 mr-2" /> + {t("filter.bulkUploadButton")} + </Button> + )} + <Button + variant="outline" + onClick={handleRefresh} + disabled={!projNo || isRefreshing} + > + <RefreshCw className={`h-4 w-4 ${isRefreshing ? "animate-spin" : ""}`} /> + </Button> + </div> + </CardContent> + </Card> + + {/* 도면 리스트 테이블 */} + {projNo && vendorInfo && ( + <Card> + <CardHeader> + <CardTitle> + {t("drawingList.title")} + {filteredDrawings.length > 0 && ` ${t("drawingList.count", { count: filteredDrawings.length })}`} + </CardTitle> + </CardHeader> + <CardContent className="overflow-x-auto"> + <DrawingListTable + columns={ + vendorInfo.drawingKind === "B4" + ? (createGttDrawingListColumns({ documentType, lng, t }) as unknown as typeof drawingListColumns) + : (drawingListColumns(lng, t) as unknown as typeof drawingListColumns) + } + data={filteredDrawings} + onRowClick={handleDrawingClick} + /> + </CardContent> + </Card> + )} + + {/* 상세도면 다이얼로그 */} + {vendorInfo && ( + <DetailDrawingDialog + drawing={selectedDrawing} + open={dialogOpen} + onOpenChange={setDialogOpen} + vendorCode={vendorInfo.vendorCode} + userId={vendorInfo.userId} + userName={vendorInfo.userName} + userEmail={vendorInfo.email} + drawingKind={vendorInfo.drawingKind} + lng={lng} + /> + )} + + {/* B4 일괄 업로드 다이얼로그 (V2) */} + {/* V2: MatchBatchFileDwg+MatchBatchFileDwgEdit API 사용, 별도의 RegisterKind 선택 없이 결과값 기준으로 업로드 */} + {vendorInfo && vendorInfo.drawingKind === "B4" && projNo && ( + <B4BulkUploadDialogV3 + open={bulkUploadDialogOpen} + onOpenChange={setBulkUploadDialogOpen} + projectNo={projNo} + userId={vendorInfo.userId} + userName={vendorInfo.userName} + userEmail={vendorInfo.email} + vendorCode={vendorInfo.vendorCode} + onUploadComplete={handleBulkUploadComplete} + lng={lng} + /> + )} + {/* V1로 되돌리려면: 위의 B4BulkUploadDialogV2를 B4BulkUploadDialog로 변경하세요 */} + </div> + ); +} + |
